import uuid
import dash
import pandas as pd
from dash import html, set_props
import feffery_antd_charts as fact
import feffery_antd_components as fac
from dash.dependencies import Input, State
from feffery_dash_utils.style_utils import style
from flask import Flask

server = Flask(__name__)
app = dash.Dash(
    __name__,
    title="股票热门板块分析",
    suppress_callback_exceptions=True,
    server=server,
    update_title=None,
)

# 读取本地已写出的最新示例数据
boards_overview = pd.read_csv("./板块交易概况.csv")

boards_detail = pd.read_csv("./板块个股交易概况.csv")


def colormap(value):
    """根据涨跌幅自定义颜色"""

    colors = [
        (-4, "#00d641"),
        (-3, "#1aa448"),
        (-2, "#0e6f2f"),
        (-1, "#085421"),
        (1, "#424453"),
        (2, "#6d1414"),
        (3, "#961010"),
        (4, "#be0808"),
        "#e41414",
    ]

    for x, color in colors[:-1]:
        if value <= x:
            return color

    return colors[-1]


def render_bubble_chart():
    return fact.AntdScatter(
        id="boards-bubble-chart",
        data=boards_overview.to_dict("records"),
        xField="成交额（亿）",
        yField="涨跌幅（%）",
        sizeField="成交额（亿）",
        xAxis={
            "title": {
                "text": "成交额（亿）",
                "style": {"fontSize": 18},
            },
            "grid": {
                "line": {
                    "style": {
                        "stroke": "#eee",
                    },
                },
            },
            "line": {
                "style": {
                    "stroke": "#eee",
                },
            },
        },
        yAxis={
            "title": {
                "text": "涨跌幅（%）",
                "style": {"fontSize": 18},
            },
        },
        size=[16, 45],
        label={
            "formatter": {"func": "(item) => item.板块名称"},
            "offsetY": 12,
            "style": {
                "fontSize": 12,
                "fontWeight": "bold",
                "fontFamily": "Microsoft YaHei",
                "lineWidth": 2,
                "stroke": "#262626",
                "fill": "white",
                "cursor": "pointer",
            },
        },
        pointStyle={
            "func": """(e) => {
                                    console.log(e)
                                    return {
                                        fill: e["涨跌幅（%）"] >= 0 ? "r(0.4, 0.3, 0.7) 0:rgba(255,255,255,0.5) 1:#be0808" : "r(0.4, 0.3, 0.7) 0:rgba(255,255,255,0.5) 1:#00d641",
                                        cursor: "pointer",
                                        stroke: false
                                    }
                            }"""
        },
        brush={
            "enabled": True,
            "button": {"text": "重置视角"},
        },
        quadrant={
            "xBaseline": boards_overview["成交额（亿）"].median(),
            "yBaseline": 0,
            "lineStyle": {
                "lineDash": [4, 2],
                "lineWidth": 2,
            },
            "regionStyle": [
                {"fill": "#cf1322", "fillOpacity": 0.4},
                {
                    "fill": "#595959",
                },
                {"fill": "#52c41a", "fillOpacity": 0.4},
                {
                    "fill": "#595959",
                },
            ],
        },
        annotations=[
            {
                "type": "text",
                "content": "热门强势板块",
                "position": ["max", "max"],
                "autoAdjust": True,
                "style": {
                    "textAlign": "end",
                    "textBaseline": "top",
                    "fontSize": 18,
                    "fontWeight": "bold",
                    "fontFamily": "Microsoft YaHei",
                    "lineWidth": 2,
                    "stroke": "#262626",
                    "fill": "white",
                },
            },
            {
                "type": "text",
                "content": "热门劣势板块",
                "position": ["max", "min"],
                "autoAdjust": True,
                "style": {
                    "textAlign": "end",
                    "textBaseline": "bottom",
                    "fontSize": 18,
                    "fontWeight": "bold",
                    "fontFamily": "Microsoft YaHei",
                    "lineWidth": 2,
                    "stroke": "#262626",
                    "fill": "white",
                },
            },
            {
                "type": "text",
                "content": "冷门劣势板块",
                "position": ["min", "min"],
                "autoAdjust": True,
                "style": {
                    "textAlign": "start",
                    "textBaseline": "bottom",
                    "fontSize": 18,
                    "fontWeight": "bold",
                    "fontFamily": "Microsoft YaHei",
                    "lineWidth": 2,
                    "stroke": "#262626",
                    "fill": "white",
                },
            },
            {
                "type": "text",
                "content": "冷门强势板块",
                "position": ["min", "max"],
                "autoAdjust": True,
                "style": {
                    "textAlign": "start",
                    "textBaseline": "top",
                    "fontSize": 18,
                    "fontWeight": "bold",
                    "fontFamily": "Microsoft YaHei",
                    "lineWidth": 2,
                    "stroke": "#262626",
                    "fill": "white",
                },
            },
        ],
        limitInPlot=False,
        padding=50,
    )


def render_layout():
    return html.Div(
        [
            fac.AntdFlex(
                [
                    fac.AntdSpace(
                        [
                            # 层级指示器
                            fac.AntdBreadcrumb(
                                id="levels-indicator",
                                items=[{"title": "板块层级", "key": "板块层级"}],
                                style=style(color="black"),
                            ),
                            fac.AntdText(
                                "操作提示：双击板块对应散点进行下钻分析",
                                id="chart-operation-tips",
                                type="secondary",
                            ),
                        ],
                        size=0,
                        direction="vertical",
                    ),
                    html.Div(
                        render_bubble_chart(),
                        id="chart-container",
                        style=style(height="100%"),
                    ),
                ],
                vertical=True,
                gap=5,
                style=style(height="calc(100vh - 120px)"),
            )
        ],
        style=style(padding="60px 120px"),
    )


app.layout = render_layout


@app.callback(Input("boards-bubble-chart", "recentlyPointDoubleClickRecord"))
def drill_down_board(record):
    """监听板块散点图的双击事件，控制对应板块个股的渲染"""

    # 针对有效的点击事件
    if record and record.get("data"):
        # 提取目标板块
        target_board = record["data"]["板块名称"]

        # 提取对应目标板块的数据子集，构建下一级的个股散点图
        target_board_detail = boards_detail.query("所属板块=='{}'".format(target_board))

        # 更新图表
        set_props(
            "chart-container",
            {
                "children": fact.AntdTreemap(
                    key=str(uuid.uuid4()),
                    data={
                        "name": "root",
                        "children": [
                            {
                                "name": row.股票名称,
                                "value": row.成交额,
                                "label": f"{row.股票名称}\n{row.股票代码}\n成交额：{round(row.成交额, 3)}亿\n涨跌幅：{round(row.涨跌幅, 3)}%",
                                "color": colormap(row.涨跌幅),
                                "children": [
                                    {
                                        "name": row.股票名称,
                                        "value": row.成交额,
                                        "label": f"{row.股票名称}\n{row.股票代码}\n成交额：{round(row.成交额, 3)}亿\n涨跌幅：{round(row.涨跌幅, 3)}%",
                                        "color": colormap(row.涨跌幅),
                                    }
                                ],
                            }
                            for row in (
                                target_board_detail.rename(
                                    columns={"成交额（亿）": "成交额"}
                                ).itertuples()
                            )
                        ],
                    },
                    colorField="name",
                    rawFields=["color", "label"],
                    color={"func": "(item) => item.color"},
                    label={
                        "style": {
                            "fontSize": 14,
                            "fontWeight": "bold",
                            "fontFamily": "Microsoft YaHei",
                            "lineWidth": 2,
                            "stroke": "#262626",
                        },
                        "formatter": {"func": "(item) => item.label"},
                    },
                    rectStyle={
                        "stroke": "black",
                        "lineWidth": 2,
                    },
                    interactions=[
                        {"type": "view-zoom"},
                        {"type": "drag-move"},
                    ],
                    legend=False,
                    padding=0,
                )
            },
        )
        # 更新指示器
        set_props(
            "levels-indicator",
            {
                "items": [
                    {"title": "板块层级", "key": "板块层级"},
                    {"title": target_board, "key": target_board},
                ]
            },
        )
        # 更新操作提示
        set_props(
            "chart-operation-tips",
            {"children": "操作提示：可通过鼠标点击拖拽图表，通用鼠标滚轮放缩查看图表"},
        )


@app.callback(
    Input("levels-indicator", "clickedItem"), State("levels-indicator", "items")
)
def switch_to_root(clickedItem, items):
    """控制点击层级指示器返回顶层"""

    if len(items) == 2:
        # 更新图表
        set_props("chart-container", {"children": render_bubble_chart()})
        # 更新指示器
        set_props("levels-indicator", {"items": [items[0]]})
        # 更新操作提示
        set_props(
            "chart-operation-tips",
            {"children": "操作提示：双击板块对应散点进行下钻分析"},
        )


if __name__ == "__main__":
    # app.run(host='0.0.0.0', debug=False)
    app.run_server(host='0.0.0.0', debug=False)
